package com.tbit.uqbike.service.business.impl;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.collection.ListUtil;
import cn.hutool.core.util.StrUtil;
import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import com.tbit.common.entity.view.PageResult;
import com.tbit.uqbike.constant.MapConstant;
import com.tbit.uqbike.constant.RoleConfig;
import com.tbit.uqbike.constant.RoleConfigConstant;
import com.tbit.uqbike.enums.SystemCodeEnum;
import com.tbit.uqbike.manager.exception.BaseException;
import com.tbit.uqbike.object.business.CalibrateUtils;
import com.tbit.uqbike.object.pojo.*;
import com.tbit.uqbike.object.pojo.dto.OperatingMachineDTO;
import com.tbit.uqbike.object.pojo.dto.OperatingMapMonitoringDTO;
import com.tbit.uqbike.object.pojo.vo.OperatingMapMachineStatus;
import com.tbit.uqbike.object.pojo.vo.OperatingMonitorV2VO;
import com.tbit.uqbike.object.value.LoginInfo;
import com.tbit.uqbike.service.base.CalibrateService;
import com.tbit.uqbike.service.business.DetentionMachineBoundService;
import com.tbit.uqbike.service.business.OperatingMapService;
import com.tbit.uqbike.service.business.StockMachineService;
import com.tbit.uqbike.service.business.VerticalParkLogService;
import com.tbit.uqbike.service.rpcservice.MachineStatusService;
import com.tbit.uqbike.webmanager.controller.BaseController;
import com.tbit.uqbike.webmanager.dao.core.*;
import com.tbit.uqbike.webmanager.dao.log.RecoveryLogMapper;
import com.tbit.uqbike.webmanager.util.IntegerUtil;
import com.tbit.uqbike.webmanager.util.ServletUtils;
import com.tbit.uqbike.webmanager.util.StringUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.util.*;
import java.util.concurrent.locks.ReentrantLock;
import java.util.stream.Collectors;


/**
 * 车辆监控-运营地图
 *
 * @Author:BUGTian
 * @DATE: 2022/11/7
 */
@Slf4j
@Service
public class OperatingMapServiceImpl implements OperatingMapService {

    private static final ReentrantLock REENTRANT_LOCK = new ReentrantLock();
    @Autowired
    private MachineDao machineDao;
    @Autowired
    private CalibrateService calibrateService;
    @Autowired
    private AccountUserDao accountUserDao;
    @Autowired
    private AccountConfigDao accountConfigDao;
    @Autowired
    private MachineSuperviseDao machineSuperviseDao;
    @Autowired
    private StockMachineService stockMachineService;
    @Autowired
    private DetentionMachineBoundService detentionMachineBoundService;
    @Autowired
    private AccountDao accountDao;
    @Autowired
    private RoleDao roleDao;
 	@Autowired
    private VerticalParkLogService verticalParkLogService;
    @Autowired
    private MachineStatusService machineStatusService;
    @Resource
    private RecoveryLogMapper recoveryLogMapper;
    @Resource
    private GeoRegionDao geoRegionDao;
    @Resource
    private MachineBorrowDao machineBorrowDao;
    @Resource
    private StockMachineMapper stockMachineMapper;
    @Resource
    private DetentionMachineBoundMapper detentionMachineBoundMapper;


    @Override
    public List<OperatingMapMachineStatus> operatingMapMonitoring(Integer adAccountId, Integer mapType, String regionIds, Integer machineType) {
        Account byAccountId = accountDao.getByAccountId(adAccountId);
        //兼容监管角色
        List<Integer> machineIdList = superviseMachine(adAccountId);

        // 兼容异常还车垂直停车
        List<VerticalParkLog> verticalParkLogList = StrUtil.isNotBlank(regionIds)
                ? verticalParkLogService.getMachineRegionIds(regionIds) // 有分区
                : verticalParkLogService.getMachineAccountId(adAccountId); // 区域
        List<String> verticalParkMachineNOList = verticalParkLogList.stream().map(VerticalParkLog::getMachineNO).collect(Collectors.toList());

        OperatingMachineDTO operatingMachineDTO = new OperatingMachineDTO();
        operatingMachineDTO.setAdAccountId(adAccountId);
        operatingMachineDTO.setRegionIds(getRegionIdList(regionIds));
        operatingMachineDTO.setMachineType(machineType);
        operatingMachineDTO.setMachineIdList(machineIdList);

        List<OperatingMapMachineStatus> list = machineDao.operatingMapMachineStatus(operatingMachineDTO);
        for (OperatingMapMachineStatus operatingMapMachineStatus : list) {
            // 兼容异常还车垂直停车
            operatingMapMachineStatus.setVerticalPark(verticalParkMachineNOList.contains(operatingMapMachineStatus.getMachineNO()));
            operatingMapMachineStatus.judgeOnline();
        }
        List<Integer> noWarehouseId = stockMachineService.getNoWarehouseId(adAccountId);
        List<MachineStatus> machineStatusList = machineStatusService.getByAccountId(adAccountId);
        Map<Integer, Boolean> machineStatusMap = machineStatusList.stream()
                .filter(machineStatus -> !IntegerUtil.getLockState(machineStatus.getTerStatus()))
                .collect(Collectors.toMap(
                        MachineStatus::getMachineId,
                        machineStatus -> !IntegerUtil.getLockState(machineStatus.getTerStatus())
                ));
        // 使用 Stream 进行遍历和修改字段
        list.forEach(obj -> {
            if (noWarehouseId.contains(obj.getMachineId())) {
                // 符合条件，修改字段的操作
                obj.setWarehouseName(byAccountId.getName() + "默认仓库");
                obj.setWarehouseId(0);
            }
            Boolean isLock = machineStatusMap.get(obj.getMachineId());
            if (!obj.isRiding() && !isLock){
                obj.setMachineUnLock(true);
                    } else {
                        obj.setMachineUnLock(false);
                    }
                });

        CalibrateUtils.calibrateObjects(calibrateService, list, mapType);
        return list;
    }


    @Override
    public PageResult<OperatingMapMachineStatus> operatingMapMonitoringPage(Integer mapType, Integer page, Integer size, OperatingMachineDTO operatingMachineDTO) {
        // 兼容监管角色
        List<Integer> machineIdList = superviseMachine(operatingMachineDTO.getAdAccountId());
        operatingMachineDTO.setMachineIdList(machineIdList);

        // 兼容异常还车垂直停车
        List<VerticalParkLog> verticalParkLogList = CollUtil.isNotEmpty(operatingMachineDTO.getRegionIds())
                ? verticalParkLogService.getMachineRegionId(operatingMachineDTO.getRegionIds()) // 有分区
                : verticalParkLogService.getMachineAccountId(operatingMachineDTO.getAdAccountId()); // 区域

        List<String> verticalParkMachineNOList = CollUtil.isNotEmpty(verticalParkLogList)
                ? verticalParkLogList.stream().map(VerticalParkLog::getMachineNO).collect(Collectors.toList())
                : Collections.singletonList("-1");
        if (Objects.equals(operatingMachineDTO.getType(), 37)) { // 异常还车垂直停车
            operatingMachineDTO.setMachineNOList(verticalParkMachineNOList);
        }

        PageInfo<OperatingMapMachineStatus> pageInfo = PageHelper.startPage(page, size).doSelectPageInfo(() -> machineDao.operatingMapMachineStatus(operatingMachineDTO));
        List<OperatingMapMachineStatus> list = pageInfo.getList();
        for (OperatingMapMachineStatus operatingMapMachineStatus : list) {
            // 兼容异常还车垂直停车
            operatingMapMachineStatus.setVerticalPark(verticalParkMachineNOList.contains(operatingMapMachineStatus.getMachineNO()));

            operatingMapMachineStatus.judgeOnline();
            operatingMapMachineStatus.parseState();
        }

        CalibrateUtils.calibrateObjects(calibrateService, list, mapType);
        return new PageResult<>(page, size, pageInfo.getTotal(), pageInfo.getPages(), list);
    }

    @Override
    public List<OperatingMapMachineStatus> getMonitoringByAccountId(Integer accountId, Integer sortType, String userCode) {
        OperatingMachineDTO operatingMachineDTO = new OperatingMachineDTO();
        operatingMachineDTO.setAdAccountId(accountId);
        operatingMachineDTO.setSortType(sortType);
        if (!StringUtils.isEmpty(userCode)){
            Machine machine = machineDao.getByUserCode(userCode);
            if (machine == null){
                throw new BaseException(SystemCodeEnum.MACHINE_NOT_EXIST);
            }
            operatingMachineDTO.setMachineId(machine.getMachineId());
        }
        List<OperatingMapMachineStatus> list = machineDao.getMonitoringByAccountId(operatingMachineDTO);
        for (OperatingMapMachineStatus operatingMapMachineStatus : list) {
            operatingMapMachineStatus.judgeOnline();
            operatingMapMachineStatus.parseState();
        }

        CalibrateUtils.calibrateObjects(calibrateService, list, MapConstant.MAP_GOOGLE);
        return list;
    }


    /**
     * 查询车辆监控数据
     * @param dto dto
     * @return PageResult<OperatingMapMachineStatus>
     */
    @Override
    public List<OperatingMonitorV2VO> operatingMapMonitoringV2(OperatingMapMonitoringDTO dto) {
        /*兼容监管角色*/
        dto.setMachineIds(superviseMachine(dto.getAccountId()));
        dto.conversion();

        /*判断是否有回收状态*/
        if (Objects.equals(dto.getCirculationStatus(), 0)){
            List<Integer> machineIds = recoveryLogMapper.selectByMachineIdList(dto.getAccountId(), dto.getMachineIds());
            if (CollectionUtil.isEmpty(machineIds)){
                return new ArrayList<>();
            }
            dto.setMachineIds(machineIds);
        }

        if (Objects.nonNull(dto.getCirculationStatus()) && 3 == dto.getCirculationStatus()){
            /*查询区域下所有分区、查询分区下车辆id*/
            List<GeoRegion> reginon = geoRegionDao.getReginon(dto.getAccountId());
            if (CollectionUtil.isNotEmpty(reginon)){
                dto.setRegionIdList(reginon.stream().map(GeoRegion::getRegionId).collect(Collectors.toList()));
            }
        }

        List<OperatingMonitorV2VO> operatingMonitorV2VOS = machineDao.operatingMapMonitoringV2(dto);
        CalibrateUtils.calibrateObjects(calibrateService, operatingMonitorV2VOS, MapConstant.MAP_GOOGLE);

        return operatingMonitorV2VOS;
    }

    /**
     * 分页查询车辆监控数据
     * @param dto dto
     * @return PageResult<OperatingMonitorV2VO>
     */
    @Override
    public PageResult<OperatingMonitorV2VO> operatingMapMonitoringV2Page(OperatingMapMonitoringDTO dto) {
        /*兼容监管角色*/
        dto.setMachineIds(superviseMachine(dto.getAccountId()));
        dto.conversion();
        /*判断是否有回收状态*/
        if (Objects.equals(dto.getCirculationStatus(), 0)){
            List<Integer> machineIds = recoveryLogMapper.selectByMachineIdList(dto.getAccountId(), dto.getMachineIds());
            if (CollectionUtil.isEmpty(machineIds)){
                return new PageResult<>();
            }
            dto.setMachineIds(machineIds);
        }
        if (Objects.nonNull(dto.getCirculationStatus()) && 3 == dto.getCirculationStatus()){
            /*查询区域下所有分区*/
            List<GeoRegion> reginon = geoRegionDao.getReginon(dto.getAccountId());
            if (CollectionUtil.isNotEmpty(reginon)){
                dto.setRegionIdList(reginon.stream().map(GeoRegion::getRegionId).collect(Collectors.toList()));
            }
        }
        PageInfo<OperatingMonitorV2VO> pageInfo = PageHelper.startPage(dto.getPage(), dto.getSize()).doSelectPageInfo(() -> machineDao.operatingMapMonitoringV2(dto));
        CalibrateUtils.calibrateObjects(calibrateService, pageInfo.getList(), MapConstant.MAP_GOOGLE);
        return new PageResult<>(dto.getPage(), dto.getSize(), pageInfo.getTotal(), pageInfo.getPages(), pageInfo.getList());
    }


    @Override
    public OperatingMapMachineStatus queryMachineStatus(Integer machineId) {
        OperatingMachineDTO operatingMachineDTO = new OperatingMachineDTO();
        operatingMachineDTO.setMachineId(machineId);
        List<OperatingMapMachineStatus> operatingMapMachineStatusList = machineDao.operatingMapMachineStatus(operatingMachineDTO);
        OperatingMapMachineStatus operatingMapMachineStatus = operatingMapMachineStatusList.get(0);
        if (operatingMapMachineStatus != null) {
            operatingMapMachineStatus.judgeOnline();
            operatingMapMachineStatus.parseState();
            Integer brandIdByAccountIdV2 = accountDao.getBrandIdByAccountIdV2(operatingMapMachineStatus.getAccountId());
            operatingMapMachineStatus.setBrandId(brandIdByAccountIdV2);

        }
        return operatingMapMachineStatus;
    }

    /**
     * 监管人员返回车辆
     *
     * @param adAccountId 区域id
     * @return
     */
    private List<Integer> superviseMachine(Integer adAccountId) {
        List<Integer> machineIdList = new LinkedList<>();
        LoginInfo login = BaseController.getLogin(ServletUtils.getRequest());
        Integer roleId = login.getRoles().stream().map(Role::getRoleId).findFirst().orElse(null);
        //根据唯一索引获取账号绑定的角色
        if (roleId != null) {
            RoleConfig roleConfig = roleDao.selectRoleConfigByKeyAndRoleId(roleId, RoleConfigConstant.SUPERVISE_MACHINE);
            if (roleConfig == null || Objects.equals(0, roleConfig.getParamValue())) {
                return machineIdList;
            }
            //如果是监管人员
            AccountConfig accountConfig = accountConfigDao.getById(adAccountId);
            Integer virtualCarNum = accountConfig == null ? 0 : accountConfig.getVirtualCarNum() == null ? 0 : accountConfig.getVirtualCarNum();
            if (virtualCarNum == 0) {
                //如果为0或者没配置，默认全部车辆都不给看
                machineIdList.add(-2);
                return machineIdList;
            }
            try {
                //防止两个接口第一次同时请求往监管人员绑定车辆表重复插入数据
                REENTRANT_LOCK.lock();
                machineIdList = machineSuperviseDao.queryMachineIdByAccountId(adAccountId);
                if (machineIdList.size() != virtualCarNum) {
                    List<Integer> allMachineId = machineDao.selectMachineIdsByAccountIds(ListUtil.of(adAccountId));
                    machineIdList = chooseReturnMachines(allMachineId, virtualCarNum);
                    List<Machinesupervise> machineSuperviseList = new LinkedList<>();
                    Date date = new Date();
                    machineSuperviseDao.deleteById(adAccountId);
                    for (Integer id : machineIdList) {
                        machineSuperviseList.add(new Machinesupervise(adAccountId, id, date));
                    }
                    machineSuperviseDao.insertBatch(machineSuperviseList);
                }
            } catch (Exception e) {
                log.error(e.getMessage(), e);
            } finally {
                REENTRANT_LOCK.unlock();
            }
        }
        return machineIdList;
    }

    /**
     * 选择返回的车辆id (散列选择)
     *
     * @param machines      车辆列表
     * @param virtualCarNum 监管绑定车辆数
     */
    List<Integer> chooseReturnMachines(List<Integer> machines, Integer virtualCarNum) {
        int machineSize = machines.size();
        virtualCarNum = machineSize > virtualCarNum ? virtualCarNum : machineSize;
        if (machineSize == virtualCarNum) {
            return machines;
        }
        List<Integer> newMachineList = new LinkedList<>();
        for (int i = 0, j = machineSize - 1, count = 0; i < j; i++, j--, count += 2) {
            newMachineList.add(machines.get(i));
            if (newMachineList.size() == virtualCarNum) {
                break;
            }
            newMachineList.add(machines.get(j));
            if (newMachineList.size() == virtualCarNum) {
                break;
            }
        }
        return newMachineList;
    }

    /**
     * 获取分区id
     *
     * @param regionIds
     * @return
     */
    List<Integer> getRegionIdList(String regionIds) {
        if (StringUtils.isEmpty(regionIds)) {
            return null;
        }
        List<Integer> list = new LinkedList<>();
        String[] split = regionIds.split(",");
        try {
            for (String s : split) {
                list.add(Integer.parseInt(s));
            }
        } catch (Exception e) {
            throw new BaseException("分区id有误");
        }
        return list;
    }


//    /**
//     * 过滤掉骑行中、扣押、回收车辆状态的车辆
//     * @param machineIds    车辆id
//     * @param regionIds     分区id
//     * @param accountId     区域id
//     * @return List<Integer>
//     */
//    private List<Integer> filter(List<Integer> machineIds, List<Integer> regionIds, Integer accountId){
//        Set<Integer> machineIdsToExclude = Stream.of(
//                        machineBorrowDao.getByRegionId(regionIds),
//                        stockMachineMapper.getByRegionId(regionIds),
//                        detentionMachineBoundMapper.selectMachineIdByAccountId(accountId)
//                ).flatMap(Collection::stream)
//                .collect(Collectors.toSet());
//
//        machineIds.removeIf(machineIdsToExclude::contains);
//        return machineIds;
//    }


}
